package ml.exp;

import android.annotation.SuppressLint;
import android.content.Context;
import android.opengl.EGL14;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.Map;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;

//VideoSystem 核心组件，他会启动一个线程，用于准备视频Buffer
public class MediaSystem {
    static Object lockstatic = new Object();
    static MediaThreadObject g_instance = null;
    @SuppressLint("StaticFieldLeak")
    static Context g_context;

    //启动VideoSystem
    public static void System_Start(Context context) {

        synchronized (lockstatic) {

            g_context = context;
            if (g_instance != null) {
                Log.w("==E==", "videosystem:Start() already start.");
                return;
            }


            g_instance = new MediaThreadObject();


            Thread t = new Thread(g_instance);
            t.start();
            Log.w("==E==", "videosystem:Start().");

        }
    }

    public static boolean System_IsOpen() {
        synchronized (lockstatic) {
            return g_instance != null;
        }
    }

    //停止VideoSystem
    public static void System_Stop() {
        synchronized (lockstatic) {
            if (g_instance == null) {
                Log.w("==E==", "glthread:Stop() already stop.");
                return;
            }


            g_instance.SelfStop();
            g_instance = null;
            g_context = null;
            Log.w("==E==", "glthread:Stop().");
        }
    }

    public static void System_Pause() {
        synchronized (lockstatic) {
            if (g_instance == null) {
                throw new RuntimeException("MediaSystem.Pause Error.MediaSystem Not Start.");
            }
            g_instance.Pause();
        }
    }

    public static void System_Resume() {
        synchronized (lockstatic) {
            if (g_instance == null) {
                throw new RuntimeException("MediaSystem.Resume Error.MediaSystem Not Start.");
            }
            g_instance.Resume();
        }
    }

    public static boolean System_IsPause() {
        synchronized (lockstatic) {
            if (g_instance == null) {
                throw new RuntimeException("MediaSystem.IsPause Error.MediaSystem Not Start.");
            }
            return g_instance.IsPause();
        }
    }

    public static int Media_Open(int mode, String name, boolean autoplay) {
        synchronized (lockstatic) {
            if (g_instance == null) {
                throw new RuntimeException("MediaSystem.OpenMedia Error.MediaSystem Not Start.");
            }
            Media media = new Media(g_context);
            if(mode==1)
            {
                media.Open(Media.ResMode.FromAssets, name, autoplay);
            }
            else if(mode==2)
            {
                media.Open(Media.ResMode.FromUrl, name, autoplay);
            }
            else
            {
                throw new RuntimeException("Error Mode.");
            }
            g_instance.SetMedia(media);
            return media._id;
        }
    }

    public static void CloseMediaAndView(int mediaid) {
        synchronized (lockstatic) {
            if (g_instance == null) {
                throw new RuntimeException("MediaSystem.CloseMedia Error.MediaSystem Not Start.");
            }
            g_instance.RemoveMedia(mediaid);//同时移除 Media 和他的 View
        }
    }

    //打开MediaView，如果你只要声音不要图像，就不需要干这个
    //width =0 ， height=0 就会自动决定尺寸

    public static void Media_SetVolume(int mediaid, float vol) {
        synchronized (lockstatic) {
            if (g_instance == null) {
                throw new RuntimeException("MediaSystem.GetMediaViewBuffer Error.MediaSystem Not Start.");
            }
            g_instance.GetPlayerObj(mediaid).media.SetVolume(vol);
        }
    }

    public static void Media_SetLoop(int mediaid, boolean loop) {
        synchronized (lockstatic) {
            if (g_instance == null) {
                throw new RuntimeException("MediaSystem.GetMediaViewBuffer Error.MediaSystem Not Start.");
            }
            g_instance.GetPlayerObj(mediaid).media.SetLoop(loop);
        }
    }

    public static void Media_OpenView(int mediaid, int width, int height) {
        synchronized (lockstatic) {
            if (g_instance == null) {
                throw new RuntimeException("MediaSystem.SetMediaView Error.MediaSystem Not Start.");
            }
            g_instance.SetMediaView(mediaid, width, height);
        }
    }

    public static boolean Media_HasView(int mediaid) {
        synchronized (lockstatic) {
            if (g_instance == null) {
                throw new RuntimeException("MediaSystem.Media_HasView Error.MediaSystem Not Start.");
            }

            return g_instance.GetPlayerObj(mediaid).view != null;
        }
    }

    public static ByteBuffer Media_GetViewBuffer(int mediaid) {
        synchronized (lockstatic) {
            if (g_instance == null) {
                Log.e("==E==", "MediaSystem.GetMediaViewBuffer Error.MediaSystem Not Start.");
                return null;
            }
            MediaThreadObject.PlayObject obj = g_instance.GetPlayerObj(mediaid);
            if (obj == null || obj.view == null)
                return null;
            return obj.view.GetBuffer();
        }
    }

    public static int Media_GetViewWidth(int mediaid) {
        synchronized (lockstatic) {
            if (g_instance == null) {
                Log.e("==E==", "MediaSystem.Media_GetViewWidth Error.MediaSystem Not Start.");
                return 0;
            }
            MediaThreadObject.PlayObject obj = g_instance.GetPlayerObj(mediaid);
            if (obj == null || obj.view == null)
                return 0;
            return obj.view.GetOutputWidth();
        }
    }

    public static int Media_GetViewHeight(int mediaid) {
        if (g_instance == null) {
            Log.e("==E==", "MediaSystem.Media_GetViewHeight Error.MediaSystem Not Start.");
            return 0;
        }
        MediaThreadObject.PlayObject obj = g_instance.GetPlayerObj(mediaid);
        if (obj == null || obj.view == null)
            return 0;
        return obj.view.GetOutputHeight();
    }


    static class MediaThreadObject implements Runnable {

        @Override
        public void run() {
            SelfInitGL();
            while (!m_exit) {
                long msbegin = System.currentTimeMillis();
                if (!m_pause) {//暂停跳过Update
                    SelfGLUpdate();
                }
                long msend = System.currentTimeMillis();
                long sleep = (1000 / 60) - (msend - msbegin);
                if (sleep > 0) {
                    try {
                        //FPS 60
                        Thread.sleep(sleep);
                    } catch (java.lang.InterruptedException e) {

                    }
                }
            }
            SelfGLRelease();
        }


        boolean m_exit = false;
        boolean m_pause = false;


        void SelfStop() {
            synchronized (this) {
                for (Map.Entry<Integer, PlayObject> entry : m_PlayerMap.entrySet()) {
                    entry.getValue().media.Stop();
                    entry.getValue().media.Release();
                    entry.getValue().media = null;
                }
            }
            m_exit = true;
        }


        public void Pause() {
            synchronized (this) {
                for (Map.Entry<Integer, PlayObject> entry : m_PlayerMap.entrySet()) {
                    entry.getValue().media.Pause();
                }
            }
        }

        public void Resume() {
            synchronized (this) {
                for (Map.Entry<Integer, PlayObject> entry : m_PlayerMap.entrySet()) {
                    entry.getValue().media.Resume();
                }
            }
        }

        public boolean IsPause() {
            return this.m_pause;
        }


        public class PlayObject {
            public Media media;//media
            public MediaGLView view;

        }

        Map<Integer, PlayObject> m_PlayerMap = new HashMap<>();

        //创建一个指定尺寸的Buffer,可以给0
        public void SetMedia(Media player) {
            PlayObject obj = new PlayObject();
            obj.media = player;
            obj.view = null;
            synchronized (this) {
                m_PlayerMap.put(player.GetID(), obj);
            }
        }

        public void SetMediaView(int meidaid, int width, int height) {
            synchronized (this) {
                PlayObject obj = m_PlayerMap.get(meidaid);
                obj.view = new MediaGLView(width, height);
            }

        }


        public void RemoveMedia(int meidaid) {
            synchronized (this) {
                PlayObject obj = m_PlayerMap.get(meidaid);
                obj.media.Release();
                //m_PlayerMap.remove(meidaid);
            }
        }


        public PlayObject GetPlayerObj(int id) {
            synchronized (this) {
                return m_PlayerMap.get(id);
            }
        }


        //////////////////////////////////////////////////////////////////////////
        //初始化GL环境
        EGL10 egl;
        EGLContext eglContext;

        private float[] triangleCoords = new float[]
                {
                        -1f, -1f, 0.0f, 0, 0,      // 1
                        1f, -1f, 0.0f, 1, 0, //2
                        -1f, 1f, 0.0f, 0, 1,//3
                        -1f, 1f, 0.0f, 0, 1,//3
                        1f, -1f, 0.0f, 1, 0,//2
                        1f, 1f, 0.0f, 1, 1,//4
                };
        private float[] uvCoords = new float[]
                {
                        0, 0,      // 1
                        1, 0, //2
                        0, 1,//3
                        0, 1,//3
                        1, 0,//2
                        1, 1,//4
                };
        private ByteBuffer vertexBuffer;
        private ByteBuffer uvBuffer;

        private String shaderCodeVS =
                "attribute vec4 vPosition;\n" +
                        "attribute vec2 vUv;\n" +
                        "varying vec2 v_texcoord;\n" +
                        "void main() {\n" +
                        "  v_texcoord =vUv;\n" +
                        "  gl_Position = vPosition;\n" +
                        "}\n";
        private static final String shaderCodeFS_OES =
                "#extension GL_OES_EGL_image_external : require\n" +
                        "precision mediump float;\n" +      // highp here doesn't seem to matter
                        "varying vec2 v_texcoord;\n" +
                        "uniform samplerExternalOES u_texture;\n" +
                        "void main() {\n" +
                        "    gl_FragColor = texture2D(u_texture, v_texcoord);\n" +
                        "}\n";
        private int shaderProgOES;

        void SelfInitGL() {
            Log.w("==E==", "glthread:SelfInitGL().");
            egl = (EGL10) EGLContext.getEGL();
            int[] eglattr = new int[]{
                    EGL10.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,  //指定渲染api类别
                    EGL10.EGL_RED_SIZE, 8,
                    EGL10.EGL_GREEN_SIZE, 8,
                    EGL10.EGL_BLUE_SIZE, 8,
                    EGL10.EGL_ALPHA_SIZE, 8,
                    EGL10.EGL_DEPTH_SIZE, 0,
                    EGL10.EGL_STENCIL_SIZE, 0,
                    EGL10.EGL_NONE      //总是以EGL10.EGL_NONE结尾
            };
            EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_NO_DISPLAY);
            int[] vers = new int[2];
            egl.eglInitialize(display, vers);
            Log.w("==E==", "EGL ver." + vers[0] + "," + vers[1]);
            int[] configsCount = new int[1];
            EGLConfig[] configs = new EGLConfig[1];

            if (!egl.eglChooseConfig(display, eglattr, configs, 1, configsCount)) {    //获取满足attributes的config个数
                throw new IllegalArgumentException("==E==Failed to choose config:" + GLUtils.getEGLErrorString(egl.eglGetError()));
            }
            int[] attrs = {
                    EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
                    EGL10.EGL_NONE
            };
            eglContext = egl.eglCreateContext(display, configs[0], EGL10.EGL_NO_CONTEXT, attrs);
            if (!egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, eglContext)) {
                throw new RuntimeException("==E==GL Make current Error" + GLUtils.getEGLErrorString(egl.eglGetError()));
            }


            {//Init VertexBuffer
                vertexBuffer = ByteBuffer.allocateDirect(triangleCoords.length * 4);
                uvBuffer = ByteBuffer.allocateDirect(uvCoords.length * 4);
                FloatBuffer fb = vertexBuffer.order(ByteOrder.nativeOrder()).asFloatBuffer();
                fb.put(triangleCoords);
                vertexBuffer.position(0);
                FloatBuffer fb2 = uvBuffer.order(ByteOrder.nativeOrder()).asFloatBuffer();
                fb2.put(uvCoords);
                uvBuffer.position(0);
            }
            {//Init Shader

                int shaderVS = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
                // 把着色器和代码关联,然后编译着色器
                GLES20.glShaderSource(shaderVS, shaderCodeVS);
                GLES20.glCompileShader(shaderVS);

                int shaderFSOES = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
                GLES20.glShaderSource(shaderFSOES, shaderCodeFS_OES);
                GLES20.glCompileShader(shaderFSOES);


                this.shaderProgOES = GLES20.glCreateProgram();
                GLES20.glAttachShader(shaderProgOES, shaderVS);
                GLES20.glAttachShader(shaderProgOES, shaderFSOES);
                GLES20.glLinkProgram(shaderProgOES);



                Log.w("==E==", "Init GLES20 quad.");
                int position = GLES20.glGetAttribLocation(shaderProgOES, "vPosition");
                GLES20.glEnableVertexAttribArray(position);

                int uv = GLES20.glGetAttribLocation(shaderProgOES, "vUv");
                GLES20.glEnableVertexAttribArray(uv);

                GLES20.glVertexAttribPointer(
                        position, 3, GLES20.GL_FLOAT,
                        false, 20, vertexBuffer);
                GLES20.glVertexAttribPointer(
                        uv, 2, GLES20.GL_FLOAT,
                        false, 8, uvBuffer);
                GLES20.glUseProgram(shaderProgOES);

            }
        }

        void SelfGLRelease() {
            Log.w("==E==", "glthread:SelfStopGL().");

            synchronized (this) {
                for (Map.Entry<Integer, PlayObject> entry : m_PlayerMap.entrySet()) {
                    PlayObject obj = entry.getValue();
                    if(obj.view==null)
                        continue;
                    obj.view.GL_Release();
                }
                m_PlayerMap.clear();
            }


            vertexBuffer = null;
            uvBuffer = null;
            //context 干掉，总清理干净了吧
            EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_NO_DISPLAY);
            egl.eglDestroyContext(display, eglContext);
            eglContext = null;
            egl = null;
        }

        //同步Texture对象
        void SelfGLUpdate() {

            //Log.w("==E==", "glthread:SelfUpdateGL().");
            //UpdateTexture
            synchronized (this) {
                int deletekey = -1;
                for (Map.Entry<Integer, PlayObject> entry : m_PlayerMap.entrySet()) {
                    PlayObject obj = entry.getValue();
                    if (obj.media == null || (!obj.media.HasInit())) {//如果有移除的,一次移除一个

                        if(obj.view!=null)
                        {
                            obj.view.GL_Release();
                        }
                        deletekey = entry.getKey();

                        continue;
                    }
                    if (obj.view == null)
                        continue;
                    ;
                    //轮询
                    if (!obj.view.HasGLInit() && obj.media.IsPrepare())//已Prepare 但是还没有设置View
                    {
                        if (obj.view.GetOutputWidth() == 0) {
                            obj.view.SetOutputSize(obj.media.GetVideoWidth(), obj.media.GetVideoWidth());
                        }
                        obj.view.GL_Init();
                        obj.media.SetTextureSurface(obj.view.InitSurfaceTexture(obj.media.GetVideoWidth(), obj.media.GetVideoWidth()));
                    }
                    obj.view.UpdateTexImage();
                    obj.view.GL_Render2Pixel(shaderProgOES);
                }
                if (deletekey >= 0) {
                    m_PlayerMap.remove(deletekey);
                }
            }
        }


    }

}
